package IC; 
  
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;


import IC.AST.*;
import IC.Parser.*; 
import IC.SemanticChecks.*;
import IC.lir.LIRProgram;
import IC.lir.LIRTranslatorVisitor;

/**
 * Compiler Main Entry Point.
 *
 */
public class Compiler
{
	public static void main(String[] args)
	{
        try 
        {        	 
        	
        	// Create a new instance of the CmdHandler Class:
        	CmdHandler.CreateNewInstance(args);
        	
        	ICClass LibraryRoot=null;
        	// Parse library file (if exist): 
            if (CmdHandler.Inst().isICLibraryFilePathExist()) {
            	LibraryRoot = parseICLibraryFile(CmdHandler.Inst().getICLibraryFilePath(),CmdHandler.Inst().isDebug());      
            	// Print Library AST (if flagged): 	          
            	if (CmdHandler.Inst().isPrintAST())
            		prettyPrint(CmdHandler.Inst().getICLibraryFilePath(), LibraryRoot); 
            }
                      	  
            // Parse IC file:
            Program ProgramRoot = parseICFile(CmdHandler.Inst().getICFilePath(),CmdHandler.Inst().isDebug());
            // Print Program AST (if flagged): 
            if (CmdHandler.Inst().isPrintAST()){      
            	System.out.println();
            	prettyPrint(CmdHandler.Inst().getICFilePath(), ProgramRoot); 
            }
            
            // Run semantic analysis:
    	    SemanticChecks.CreateNewInstance(ProgramRoot,CmdHandler.Inst().getICFilePath(),LibraryRoot);
            SemanticChecks.Inst().Run();
    		// Print symbol table and type table (if flagged):
    		if (CmdHandler.Inst().isDumpSymtb()) {
    			System.out.println();
    			SemanticChecks.Inst().printTables();
    		}
    		
    		// Create LIR program:
    		LIRProgram lirProgram = TranslateToLir(ProgramRoot);
    		// Print LIR program to file (if flagged): 
    		if (CmdHandler.Inst().isPrintLIR())
    			PrintLirProgramToFile(lirProgram);
          		
        } 
        catch (Exception e) 
        {
        	System.out.println(e.getMessage());
        }
      }
	
	 /** 
     * This function parse the an IC library file. 
     * @param LibraryICFilePath - The library file path. 
     * @param debugMode - If true parser runs in debug mode.
     * @throws Exception  
     */ 
    public static ICClass parseICLibraryFile(String LibraryICFilePath, boolean debug) throws Exception { 
        try
        {    
        	 return (ICClass) parseICFile(LibraryICFilePath, true,debug); 
        }
        catch(Exception e)
        {
        	throw new Exception(LibraryICFilePath + ":" + e.getMessage());
        }
    } 
     
    /** 
     * This function parse the an IC file.
     * @param IcFilePath - The IC file path. 
     * @param debugMode - If true parser runs in debug mode. 
     * @throws Exception 
     */ 
    public static Program parseICFile(String IcFilePath, boolean debug) throws Exception 
    { 
        try
        {    
        	return (Program) parseICFile(IcFilePath, false,debug);
        }
        catch(Exception e)
        {
        	throw new Exception(IcFilePath + ":" + e.getMessage());
        }
    }
    
    /** 
     * AST prettyPrinter. 
     * @param ICFilePath - The IC file path. 
     * @param ASTNode - The root of the AST. 
     */ 
    public static void prettyPrint(String ICFilePath, ASTNode root) {   
            PrettyPrinter printer = new PrettyPrinter(ICFilePath); 
            System.out.println((String) root.accept(printer)); 
    } 
  
    
	/** 
     * This function parse the file.  
     * @param ICFilePath - The file path. 
     * @param isICLibraryFile - If true IC file to parse is a Library IC file. 
     * @param debugMode - If true runs in debug mode. 
     * @throws Exception 
     * @return ICFile corresponding AST.
     */ 
    private static ASTNode parseICFile(String ICFilePath, boolean isICLibraryFile, boolean debug) throws Exception {  
            ASTNode Root = null; 
            Lexer lexer = new Lexer(new FileReader(ICFilePath));                 
            if (isICLibraryFile) 
            {
            	 LibraryParser LParser = new LibraryParser(lexer,debug); 
            	 Root = (ASTNode)LParser.parse().value; 
            }
            else 
            {
            	 Parser FParser = new Parser(lexer,debug); 
            	 Root = (ASTNode)FParser.parse().value; 
            }
                         
            System.out.println("Parsed " + ICFilePath + " successfully!\n");      
            return Root; 
    } 
    
    /** 
     * This function translate the AST into LIR program.  
     * @param Root - The Program AST root. 
     * @throws Exception 
     * @return LIRProgram
     */ 
    private static LIRProgram TranslateToLir(Program Root) throws Exception {
		LIRProgram lirProgram =  new LIRTranslatorVisitor(Root).Run();
		System.out.println();
		System.out.println(CmdHandler.Inst().getICFilePath() + " LIR translation passed successfully!");
		return lirProgram;
    }
    
    /** 
     * This function prints the LIR Program into a well known location file (".\\out\\FileName.lir")
     * @param program - The LIRProgram to print. 
     * @throws Exception 
     */ 
    private static void PrintLirProgramToFile(LIRProgram program) throws Exception {
		
    	String outputFilePath=null;
		int P_endIndex = CmdHandler.Inst().getICFilePath().lastIndexOf('\\');
		if (P_endIndex == -1) {
			outputFilePath = "";
		}
		
		String FileName=null;
		int F_beginIndex = CmdHandler.Inst().getICFilePath().lastIndexOf('\\');
		int F_endIndex = CmdHandler.Inst().getICFilePath().lastIndexOf('.');
		if (F_endIndex == -1) {
			FileName= null;
		}
		
		FileName=  CmdHandler.Inst().getICFilePath().substring(F_beginIndex+1, F_endIndex);
		
		outputFilePath= FileName +".lir" ;	
		
		File file = new File(outputFilePath);
		file.createNewFile();
		BufferedWriter writer = new BufferedWriter(new FileWriter(file));
		writer.write((String)program.toString());
		writer.close();
    }

}

